{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Theano tensor 模块：操作符和逐元素操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 操作符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using gpu device 1: Tesla C2075 (CNMeM is disabled)\n"
     ]
    }
   ],
   "source": [
    "import theano\n",
    "from theano import tensor as T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`tensor` 类型支持很多基本的操作："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 两个整形三维张量\n",
    "\n",
    "a, b = T.itensor3(\"a\"), T.itensor3(\"b\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 算术操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(a + TensorConstant{3})\n",
      "(TensorConstant{3} - a)\n",
      "(a * TensorConstant{3.5})\n",
      "(TensorConstant{2.20000004768} / a)\n",
      "(TensorConstant{2.20000004768} // a)\n",
      "(TensorConstant{2.20000004768} ** a)\n",
      "mod(b, a)\n"
     ]
    }
   ],
   "source": [
    "print theano.pp(a + 3)      # T.add(a, 3) -> itensor3\n",
    "print theano.pp(3 - a)      # T.sub(3, a)\n",
    "print theano.pp(a * 3.5)    # T.mul(a, 3.5) -> ftensor3 or dtensor3 (depending on casting)\n",
    "print theano.pp(2.2 / a)    # T.truediv(2.2, a)\n",
    "print theano.pp(2.2 // a)   # T.intdiv(2.2, a)\n",
    "print theano.pp(2.2**a)     # T.pow(2.2, a)\n",
    "print theano.pp(b % a)      # T.mod(b, a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 比特操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "and_(a, b)\n",
      "xor(a, TensorConstant{1})\n",
      "or_(a, b)\n",
      "invert(a)\n"
     ]
    }
   ],
   "source": [
    "print theano.pp(a & b)      # T.and_(a,b)    bitwise and (alias T.bitwise_and)\n",
    "print theano.pp(a ^ 1)      # T.xor(a,1)     bitwise xor (alias T.bitwise_xor)\n",
    "print theano.pp(a | b)      # T.or_(a,b)     bitwise or (alias T.bitwise_or)\n",
    "print theano.pp(~a)         # T.invert(a)    bitwise invert (alias T.bitwise_not)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 原地操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Theano` 不支持原地操作如 `+=` 等，`Theano` 的图优化解构会自动决定是否使用原地操作。如果需要更新变量的值，可以考虑使用共享变量 `theano.shared`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 逐元素操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类型转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`T.cast(x, dtype)` 用于类型转换："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = T.matrix()\n",
    "x_as_int = T.cast(x, 'int32')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`T.cast(x, dtype)` 的机制与 `numpy.asarray(x, dtype)` 的机制类似，只有 `dtype` 不同时才会创建新的变量： "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print x_as_int is x\n",
    "print T.cast(x, theano.config.floatX) is x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "复数取实部，虚部，角度，模：\n",
    "\n",
    "- `T.real(a)`\n",
    "- `T.imag(a)`\n",
    "- `T.angle(a)`\n",
    "- `T.abs_(a)`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 比较"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Theano` 的比较操作也是逐元素的：\n",
    "\n",
    "- `T.lt(a, b)` : <\n",
    "- `T.gt(a, b)` : >\n",
    "- `T.le(a, b)` : <=\n",
    "- `T.ge(a, b)` : >=\n",
    "- `T.eq(a, b)` : ==\n",
    "- `T.neq(a, b)` : !=\n",
    "\n",
    "`Theano` 中没有 `bool` 类型，所有的 `bool` 类型都用 `int8` 表示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "le(x, y)\n"
     ]
    }
   ],
   "source": [
    "x, y = T.dmatrices('x','y')\n",
    "\n",
    "print theano.pp(T.le(x, y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除此之外，还有另一些与 `numpy` 类似的用法：\n",
    "\n",
    "- `T.isnan(a)` : 是否 NAN\n",
    "- `T.isinf(a)` : 是否 INF\n",
    "- `T.isclose(a, b)` ：浮点数是否接近\n",
    "- `T.allclose(a, b)` ：浮点数是否很接近"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 条件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`T.switch(cond, ift, iff)` 选择 `ift (if ture)` 和 `iff (if false)`。\n",
    "\n",
    "`T.where(cond, ift, iff)` 与 `switch` 一致。\n",
    "\n",
    "`T.clip(x, min, max)` 低于 `min` 的部分变成 `min`，超过 `max` 的部分变成 `max`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数学操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "maximum(a, b)\n",
      "minimum(a, b)\n",
      "(-a)\n",
      "inv(a)\n",
      "exp(a)\n",
      "log(a) log2(a) log10(a)\n",
      "sgn(a)\n",
      "floor(a)\n",
      "ceil(a)\n",
      "round_half_away_from_zero(a)\n",
      "int64(round_half_away_from_zero(a))\n",
      "sqr(a)\n",
      "sqrt(a)\n",
      "cos(a) sin(a) tan(a)\n",
      "cosh(a) sinh(a) tanh(a)\n",
      "erf(a) erfc(a)\n",
      "erfinv(a) erfcinv(a)\n",
      "gamma(a)\n",
      "gammaln(a)\n",
      "psi(a)\n"
     ]
    }
   ],
   "source": [
    "a, b = T.matrices(\"a\", \"b\")\n",
    "\n",
    "\n",
    "print theano.pp(T.maximum(a, b))  # max(a, b)\n",
    "print theano.pp(T.minimum(a, b))  # min(a, b)\n",
    "\n",
    "print theano.pp(T.neg(a)) # -a\n",
    "print theano.pp(T.inv(a)) # 1.0/a\n",
    "\n",
    "print theano.pp(T.exp(a)) \n",
    "print theano.pp(T.log(a)), theano.pp(T.log2(a)), theano.pp(T.log10(a))       # log10(a)\n",
    "\n",
    "print theano.pp(T.sgn(a))       # sgn(a)\n",
    "print theano.pp(T.floor(a))     # floor(a)\n",
    "print theano.pp(T.ceil(a))      # ceil(a)\n",
    "print theano.pp(T.round(a))     # round(a)\n",
    "print theano.pp(T.iround(a))    # iround(a)\n",
    "\n",
    "print theano.pp(T.sqr(a))   # sqr(a)\n",
    "print theano.pp(T.sqrt(a))  # sqrt(a)\n",
    "\n",
    "print theano.pp(T.cos(a)), theano.pp(T.sin(a)), theano.pp(T.tan(a))\n",
    "print theano.pp(T.cosh(a)), theano.pp(T.sinh(a)), theano.pp(T.tanh(a))         # tan(a)\n",
    "\n",
    "print theano.pp(T.erf(a)), theano.pp(T.erfc(a)) # erf(a), erfc(a)\n",
    "print theano.pp(T.erfinv(a)), theano.pp(T.erfcinv(a))\n",
    "\n",
    "print theano.pp(T.gamma(a))    # gamma(a)\n",
    "print theano.pp(T.gammaln(a))  # log(gamma(a))\n",
    "print theano.pp(T.psi(a))      # digamma(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其中 `erf, erfc` 定义如下：\n",
    "https://en.wikipedia.org/wiki/Error_function\n",
    "\n",
    "$$\n",
    "\\operatorname{erf}(x) = \\frac{2}{\\sqrt\\pi} \\int_0^x e^{-t^2} dt\n",
    "$$\n",
    "$$\n",
    "\\begin{align}\n",
    "             \\operatorname{erfc}(x) & = 1-\\operatorname{erf}(x) \\\\\n",
    "                                    & = \\frac{2}{\\sqrt\\pi} \\int_x^{\\infty} e^{-t^2}\\,\\mathrm dt \\\\\n",
    "                                    & = e^{-x^2} \\operatorname{erfcx}(x)\n",
    "\\end{align} \n",
    "$$\n",
    "\n",
    "`erfinv, erfcinv` 为其反函数：1\n",
    "https://en.wikipedia.org/wiki/Error_function#Inverse_functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Broadcasting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://deeplearning.net/software/theano/_images/bcast.png)\n",
    "\n",
    "图示如上。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 线性代数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "矩阵乘法：`T.dot(x, y)`\n",
    "\n",
    "向量外积：`T.outer(x, y)`\n",
    "\n",
    "张量乘法：`tensordot(a, b, axes=2)`\n",
    "\n",
    "`axes` 参数表示 `a` `b` 对应要去掉的维度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 3, 4) (5, 6, 4, 3)\n",
      "(2, 5, 6)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "a = np.random.random((2,3,4))\n",
    "b = np.random.random((5,6,4,3))\n",
    "\n",
    "#tensordot\n",
    "c = np.tensordot(a, b, [[1,2],[3,2]])\n",
    "\n",
    "\n",
    "#loop replicating tensordot\n",
    "a0, a1, a2 = a.shape\n",
    "b0, b1, _, _ = b.shape\n",
    "cloop = np.zeros((a0,b0,b1))\n",
    "\n",
    "#loop over non-summed indices -- these exist\n",
    "#in the tensor product.\n",
    "for i in range(a0):\n",
    "    for j in range(b0):\n",
    "        for k in range(b1):\n",
    "            #loop over summed indices -- these don't exist\n",
    "            #in the tensor product.\n",
    "            for l in range(a1):\n",
    "                for m in range(a2):\n",
    "                    cloop[i,j,k] += a[i,l,m] * b[j,k,m,l]\n",
    "\n",
    "assert np.allclose(c, cloop)\n",
    "\n",
    "print a.shape, b.shape\n",
    "print c.shape"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
